require 'rexml/document'
require 'cgi'
require 'open-uri'

module RakutenWebService
  DEVELOPER_ID = "[your developer id]"
  module GenreSearch
    class URL_Builder
      attr_accessor :affiliateId, :callBack, :genreid, :genrepath
      attr_accessor :base, :developerId, :operation, :version
      def initialize(developerId = nil)
        @base = "http://api.rakuten.co.jp/rws/1.11/rest?"
        @operation = "GenreSearch"
        @version = "2007-04-11"
        @developerId = developerId
        @genrepath = "0"
      end

      def build
        url = @base
        raise RuntimeError, 'No developerId' unless @developerId
        raise RuntimeError, 'No GenreId' unless @genreid
        @genreid = @genreid.to_s
        url += "developerId=#{@developerId}"
        url += "&operation=#{@operation}"
        url += "&version=#{@version}"
        url += "&affiliateId=#{affiliateId}" if @affiliateId
        url += "&callBack=#{@callBack}" if @callBack
        url += "&genreId=#{CGI.escape(@genreid)}" if @genreid
        url += "&genrePath=#{CGI.escape(@genrepath)}" if @genrepath
      end
    end
    
    class Result
      attr_accessor :header, :genres
      def initialize(url)
        # get xml contents
        xml = open(url).read
        doc = REXML::Document.new xml

        # create header
        @header = RakutenWebService::GenreSearch::Result_Header.new
        doc.elements["/Response/header:Header/Args"].each do |e|
          @header.method((e.attributes["key"].downcase.gsub("-", "") + "=").to_sym).call(e.attributes["value"])
        end
        @header.status = doc.elements["/Response/header:Header/Status"].text
        @header.statusmsg = doc.elements["/Response/header:Header/StatusMsg"].text

        # create genre
        @genres = {}
        ["parent", "current"].each do |n|
          if doc.elements["/Response/Body/genreSearch:GenreSearch/#{n}"]
            @genre = RakutenWebService::GenreSearch::Result_Genre.new
            doc.elements["/Response/Body/genreSearch:GenreSearch/#{n}"].each do |i|
              next unless i.name
              @genre.method((i.name.downcase + "=").to_sym).call(i.text)
            end
            @genres[n] = @genre
          end
        end
      end
    end
    
    class Result_Header
      attr_accessor :useragent, :developerid, :apiversion, :operation, :version, :status, :statusmsg
      attr_accessor :count, :page, :first, :last, :hits, :carrier, :pagecount
      attr_accessor :sort, :availability, :field, :imageflag, :orflag, :keyword, :itemcode, :genreid, :genrepath
    end
    
    class Result_Genre
      attr_accessor :genreid, :genrename, :genrelevel
    end
  end
  
  module ItemCodeSearch
    class Result
      attr_accessor :header, :item
      def initialize(url)
        # get xml contents
        xml = open(url).read
        doc = REXML::Document.new xml

        # create header
        @header = RakutenWebService::Result_Header.new
        doc.elements["/Response/header:Header/Args"].each do |e|
          @header.method((e.attributes["key"].downcase.gsub("-", "") + "=").to_sym).call(e.attributes["value"])
        end
        @header.status = doc.elements["/Response/header:Header/Status"].text
        @header.statusmsg = doc.elements["/Response/header:Header/StatusMsg"].text

        # create items
        @item = nil
        if doc.elements["/Response/Body/itemCodeSearch:ItemCodeSearch/Item"]
          @item = RakutenWebService::Result_Item.new
          doc.elements["/Response/Body/itemCodeSearch:ItemCodeSearch/Item"].each do |i|
            next unless i.name
            @item.method((i.name.downcase + "=").to_sym).call(i.text)
          end
        end
      end
    end
    
    class URL_Builder
      attr_accessor :affiliateId, :callBack, :itemcode, :carrier
      attr_accessor :base, :developerId, :operation, :version
      def initialize(developerId = nil)
        @base = "http://api.rakuten.co.jp/rws/1.11/rest?"
        @operation = "ItemCodeSearch"
        @version = "2007-04-11"
        @developerId = developerId
      end

      def build
        url = @base
        raise RuntimeError, 'No developerId' unless @developerId
        raise RuntimeError, 'No ItemCode' unless @itemcode
        url += "developerId=#{@developerId}"
        url += "&operation=#{@operation}"
        url += "&version=#{@version}"
        url += "&affiliateId=#{affiliateId}" if @affiliateId
        url += "&callBack=#{@callBack}" if @callBack
        url += "&itemCode=#{CGI.escape(@itemcode)}" if @itemcode
        url
      end
    end
  end

  class Result_Header
    attr_accessor :useragent, :developerid, :apiversion, :operation, :version, :status, :statusmsg
    attr_accessor :count, :page, :first, :last, :hits, :carrier, :pagecount
    attr_accessor :sort, :availability, :field, :imageflag, :orflag, :keyword, :itemcode
  end

  class Result_Item
    attr_accessor :itemname, :itemcode, :itemprice, :itemcaption, :itemurl
    attr_accessor :affiliateurl, :imageflag, :smallimageurl, :mediumimageurl
    attr_accessor :availability, :taxflag, :postageflag, :creditcardflag
    attr_accessor :shopoftheyearflag, :affiliaterate, :starttime, :endtime, :carrier
    attr_accessor :reviewcount, :reviewaverage, :shopname, :shopcode, :shopurl, :genreid
  end
  
  module ItemSearch
    class Result
      attr_accessor :header, :items
      def initialize(url)
        # get xml contents
        xml = open(url).read
        doc = REXML::Document.new xml

        # create header
        @header = RakutenWebService::Result_Header.new
        doc.elements["/Response/header:Header/Args"].each do |e|
          @header.method((e.attributes["key"].downcase.gsub("-", "") + "=").to_sym).call(e.attributes["value"])
        end
        @header.status = doc.elements["/Response/header:Header/Status"].text
        @header.statusmsg = doc.elements["/Response/header:Header/StatusMsg"].text
        ["count", "page", "first", "last", "hits", "carrier", "pageCount"].each do |n|
          next unless doc.elements["/Response/Body/itemSearch:ItemSearch/#{n}"]
          @header.method((n.downcase + "=").to_sym).call(doc.elements["/Response/Body/itemSearch:ItemSearch/#{n}"].text)
        end

        # create items
        @items = []
        if doc.elements["/Response/Body/itemSearch:ItemSearch/Items"]
          doc.elements["/Response/Body/itemSearch:ItemSearch/Items"].each do |i|
            next unless i
            item = RakutenWebService::Result_Item.new
            i.each do |e|
              next unless e.name
              item.method((e.name.downcase + "=").to_sym).call(e.text)
            end
            @items << item
          end
        end
      end
    end

    class URL_Builder
      attr_accessor :affiliateId, :callBack, :keyword, :shopCode, :genreId
      attr_accessor :catalogCode, :minPrice, :maxPrice, :ngkeyword, :genreInformationFlag
      attr_accessor :base, :developerId, :operation, :hits, :page, :availability, :sort, :version
      attr_accessor :availability, :field, :carrier, :imageFlag, :orFlag, :base
      def initialize(developerId = nil)
        @base = "http://api.rakuten.co.jp/rws/1.11/rest?"
        @operation = "ItemSearch"
        @version = "2007-10-25"
        @sort = CGI.escape "+itemPrice"
        @developerId = developerId
        @hits = 30
        @page = 1
        @availability = @field = @carrier = @imageFlag = @orFlag = 0
      end

      def build
        url = @base
        raise RuntimeError, 'No developerId' unless @developerId
        url += "developerId=#{@developerId}"
        url += "&operation=#{@operation}"
        url += "&version=#{@version}"
        url += "&affiliateId=#{affiliateId}" if @affiliateId
        url += "&callBack=#{@callBack}" if @callBack
        url += "&keyword=#{CGI.escape(@keyword)}" if @keyword
        url += "&shopCode=#{shopCode}" if @shopCode
        url += "&genreId=#{genreId}" if @genreId
        url += "&catalogCode=#{@catalogCode}" if @catalogCode
        url += "&hits=#{@hits}" if @hits
        url += "&page=#{@page}" if @page
        url += "&sort=#{@sort}" if @sort
        url += "&minPrice=#{@minPrice}" if @minPrice
        url += "&maxPrice=#{@maxPrice}" if @maxPrice
        url += "&availability=#{@availability}" if @availability
        url += "&field=#{@field}" if @field
        url += "&carrier=#{@carrier}" if @carrier
        url += "&imageFlag=#{@imageFlag}" if @imageFlag
        url += "&orFlag=#{@orFlag}" if @orFlag
        url += "&NGKeyword=#{CGI.escape(@ngkeyword)}" if @ngkeyword
        url += "&genreInformationFlag=#{@genreInformationFlag}" if @genreInformationFlag
        url
      end
    end
  end
end

if __FILE__ == $0
  developerId = RakutenWebService::DEVELOPER_ID
  url = RakutenWebService::ItemSearch::URL_Builder.new developerId
  url.keyword = "カラーボックス"
  rakuten  = RakutenWebService::ItemSearch::Result.new url.build
  p rakuten.header.keyword
  rakuten.items.each do |i|
    p i.itemname
  end
  
  url = RakutenWebService::ItemCodeSearch::URL_Builder.new developerId
  url.itemcode = "kami-bungu:1392578"
  rakuten  = RakutenWebService::ItemCodeSearch::Result.new url.build
  p rakuten.item.itemname

  url = RakutenWebService::GenreSearch::URL_Builder.new developerId
  url.genreid = "111202"
  rakuten  = RakutenWebService::GenreSearch::Result.new url.build
  p rakuten.genres['current'].genrename
end
